/*//////////////////////////////////////////////////////////////////////////////
// Copyright (C) 2008 Michiel Helvensteijn                                    //
//                                                                            //
// This file is part of the Mist compiler.                                    //
//                                                                            //
// The Mist compiler is free software: you can redistribute it and/or modif   //
// it under the terms of the GNU General Public License as published by       //
// the Free Software Foundation, either version 3 of the License, or          //
// (at your option) any later version.                                        //
//                                                                            //
// The Mist compiler is distributed in the hope that it will be useful,       //
// but WITHOUT ANY WARRANTY; without even the implied warranty of             //
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the              //
// GNU General Public License for more details.                               //
//                                                                            //
// You should have received a copy of the GNU General Public License          //
// along with the Mist compiler.  If not, see <http://www.gnu.org/licenses/>. //
//////////////////////////////////////////////////////////////////////////////*/

/******************************************************************************
 * Initial Code                                                               *
 ******************************************************************************/

%top {
	#include <string>
	using std::string;
	
	#include <istream>
	using std::istream;
	
	#include <boost/shared_ptr.hpp>
	using boost::shared_ptr;
	
	#include "bisonparser.tab.hpp"
	#include "bigint.hpp"
	#include "messages.hpp"
	#include "common.hpp"
	#include "assert.hpp"
	
	struct LexerParams {
		istream* in;
		shared_ptr<Messages> messages;
		int entry_point;
	};
	
	#define YYSTYPE yy::BisonParser::semantic_type
	#define YYLTYPE yy::BisonParser::location_type
	
	#define YY_INPUT(buf, result, max_size) { \
		yyextra->in->get (buf, max_size, EOF); \
		result = yyextra->in->gcount();        \
	}
}

/******************************************************************************
 * Regex Definitions                                                          *
 ******************************************************************************/

identifier    [_a-zA-Z][_a-zA-Z0-9]*
decimalnumber [0-9][0-9]*
binarynumber  0[bB][01][01]*
hexnumber     0[xX][0-9a-fA-F][0-9a-fA-F]*
octalnumber   0[oOqQ][0-7][0-7]*

/******************************************************************************
 * Options                                                                    *
 ******************************************************************************/

%option reentrant
%option outfile="lexer.lex.cpp"
%option header-file="lexer.lex.hpp"
%option full
%option noyywrap
%option nounput
%option 8bit
%option extra-type="LexerParams*"
%option nostdinit
%option noyylineno
%option bison-bridge
%option bison-locations
%option pointer
%option nounistd
%option nodefault

/******************************************************************************
 * Start Conditions                                                           *
 ******************************************************************************/

%x comment
%x nestingcomment
%x linecomment
%s assertion

%%

 /*****************************************************************************
 * Some In-Function Code                                                      *
 ******************************************************************************/

%{
	typedef yy::BisonParser::token token;
	unsigned int nestingCommentDepth = 0;
	CodePosition commentBegin;
	
	if (yyextra->entry_point != 0) {
		int entry_point = yyextra->entry_point;
		yyextra->entry_point = 0;
		return entry_point;
	}
%}

 /*****************************************************************************
 * Lexer Tokens                                                               *
 ******************************************************************************/

 /*------------language-constructs----------------*/

if     { yylloc->step();
         yylloc->columns(yyleng);
         return token::IF;
       }

else   { yylloc->step();
         yylloc->columns(yyleng);
         return token::ELSE;
       }

while  { yylloc->step();
         yylloc->columns(yyleng);
         return token::WHILE;
       }

for    { yylloc->step();
         yylloc->columns(yyleng);
         return token::FOR;
       }

".."   { yylloc->step();
         yylloc->columns(yyleng);
         return token::RANGE_OP;
       }

int    { yylloc->step();
         yylloc->columns(yyleng);
         return token::INT;
       }

bool   { yylloc->step();
         yylloc->columns(yyleng);
         return token::BOOL;
       }

void   { yylloc->step();
         yylloc->columns(yyleng);
         return token::VOID;
       }

"^"    { yylloc->step();
         yylloc->columns(yyleng);
         return '^';
       }

"@"    { yylloc->step();
         yylloc->columns(yyleng);
         BEGIN(assertion);
         return '@';
       }

pre    { yylloc->step();
         yylloc->columns(yyleng);
         return token::PRE;
       }

post   { yylloc->step();
         yylloc->columns(yyleng);
         return token::POST;
       }

body   { yylloc->step();
         yylloc->columns(yyleng);
         return token::BODY;
       }

"("    { yylloc->step();
         yylloc->columns(yyleng);
         return '(';
       }

")"    { yylloc->step();
         yylloc->columns(yyleng);
         return ')';
       }

"{"    { yylloc->step();
         yylloc->columns(yyleng);
         return '{';
       }

"}"    { yylloc->step();
         yylloc->columns(yyleng);
         return '}';
       }

"["    { yylloc->step();
         yylloc->columns(yyleng);
         return '[';
       }

"]"    { yylloc->step();
         yylloc->columns(yyleng);
         return ']';
       }

","    { yylloc->step();
         yylloc->columns(yyleng);
         return ',';
       }

";"    { yylloc->step();
         yylloc->columns(yyleng);
         return ';';
       }

val     { yylloc->step();
         yylloc->columns(yyleng);
         return token::VAL;
       }

ref    { yylloc->step();
         yylloc->columns(yyleng);
         return token::REF;
       }

als    { yylloc->step();
         yylloc->columns(yyleng);
         return token::ALS;
       }

function { yylloc->step();
           yylloc->columns(yyleng);
           return token::FUNCTION;
         }

"."    { yylloc->step();
         yylloc->columns(yyleng);
         return '.';
       }

null   { yylloc->step();
         yylloc->columns(yyleng);
         return token::NULLPTR;
       }

 /*------------operators--------------------------*/

"<-"  { yylloc->step();
        yylloc->columns(yyleng);
        return token::VAL_ASSIGN_OP;
      }

"<--" { yylloc->step();
        yylloc->columns(yyleng);
        return token::ID_ASSIGN_OP;
      }

"+"   { yylloc->step();
        yylloc->columns(yyleng);
        return '+';
      }

"-"   { yylloc->step();
        yylloc->columns(yyleng);
        return '-';
      }

"*"   { yylloc->step();
        yylloc->columns(yyleng);
        return '*';
      }

"/"   { yylloc->step();
        yylloc->columns(yyleng);
        return '/';
      }

"mod" { yylloc->step();
        yylloc->columns(yyleng);
        return token::MOD_OP;
      }

"==" { yylloc->step();
       yylloc->columns(yyleng);
       return token::ID_OP;
     }

"!==" { yylloc->step();
        yylloc->columns(yyleng);
        return token::NOT_ID_OP;
      }

"="   { yylloc->step();
        yylloc->columns(yyleng);
        return '=';
      }

"<"   { yylloc->step();
        yylloc->columns(yyleng);
        return '<';
      }

">"   { yylloc->step();
        yylloc->columns(yyleng);
        return '>';
      }

"<="  { yylloc->step();
        yylloc->columns(yyleng);
        return token::LE_OP;
      }

">="  { yylloc->step();
        yylloc->columns(yyleng);
        return token::GE_OP;
      }

"<>"  { yylloc->step();
        yylloc->columns(yyleng);
        return token::LG_OP;
      }

"<>=" { yylloc->step();
        yylloc->columns(yyleng);
        return token::LGE_OP;
      }

"!="  { yylloc->step();
        yylloc->columns(yyleng);
        return token::NE_OP;
      }

"!<"  { yylloc->step();
        yylloc->columns(yyleng);
        return token::NL_OP;
      }

"!>"  { yylloc->step();
        yylloc->columns(yyleng);
        return token::NG_OP;
      }

"!<=" { yylloc->step();
        yylloc->columns(yyleng);
        return token::NLE_OP;
      }

"!>=" { yylloc->step();
        yylloc->columns(yyleng);
        return token::NGE_OP;
      }

"!<>" { yylloc->step();
        yylloc->columns(yyleng);
        return token::NLG_OP;
      }

"!<>=" { yylloc->step();
         yylloc->columns(yyleng);
         return token::NLGE_OP;
       }

"=>"  { yylloc->step();
        yylloc->columns(yyleng);
        return token::IMPLIES_OP;
      }

and   { yylloc->step();
        yylloc->columns(yyleng);
        return token::AND_OP;
      }

or    { yylloc->step();
        yylloc->columns(yyleng);
        return token::OR_OP;
      }

"#"   { yylloc->step();
        yylloc->columns(yyleng);
        return '#';
      }

"!"   { yylloc->step();
        yylloc->columns(yyleng);
        return '!';
      }

 /*------------literals--------------------------*/

{decimalnumber} { yylloc->step();
                  yylloc->columns(yyleng);
                  yylval->number = new BigInt(yytext);
                  return token::NUMBER;
                }

{binarynumber}  { yylloc->step();
                  yylloc->columns(yyleng);
                  yylval->number = new BigInt(yytext + 2, 2);
                  return token::NUMBER;
                }

{hexnumber}     { yylloc->step();
                  yylloc->columns(yyleng);
                  yylval->number = new BigInt(yytext + 2, 16);
                  return token::NUMBER;
                }

{octalnumber}   { yylloc->step();
                  yylloc->columns(yyleng);
                  yylval->number = new BigInt(yytext + 2, 8);
                  return token::NUMBER;
                }

true     { yylloc->step();
           yylloc->columns(yyleng);
           yylval->boolean = true;
           return token::BOOL_LITERAL;
         }

false    { yylloc->step();
           yylloc->columns(yyleng);
           yylval->boolean = false;
           return token::BOOL_LITERAL;
         }

 /*------------identifier------------------------*/

{identifier} { yylloc->step();
               yylloc->columns(yyleng);
               yylval->str = new string(yytext);
               return token::IDENTIFIER;
             }

 /*------------whitespace------------------------*/

[ ]+            { yylloc->step();
                  yylloc->columns(yyleng);
                }

[\t]+           { yylloc->step();
                  yylloc->columns(yyleng);
                }

<assertion>[\n] { yylloc->step();
                  yylloc->lines(yyleng);
                  BEGIN(INITIAL);
                  return token::ASSERTION_END;
                }

[\n]+           { yylloc->step();
                  yylloc->lines(yyleng);
                }

 /*------------comments--------------------------*/

"/*"                  { yylloc->step();
                        commentBegin = yylloc->begin;
                        yylloc->columns(yyleng);
                        BEGIN(comment);
                      }

<comment><<EOF>>      { if (yyextra->messages)
                        	yyextra->messages->add("unterminated-comment", CodeLocation(commentBegin, yylloc->end));
                        return token::END;
                      }

<comment>[^*\n]*      { yylloc->step();
                        yylloc->columns(yyleng);
                      }

<comment>"*"+[^*/\n]* { yylloc->step();
                        yylloc->columns(yyleng);
                      }

<comment>[\n]+        { yylloc->step();
                        yylloc->lines(yyleng);
                      }

<comment>"*/"         { yylloc->step();
                        yylloc->columns(yyleng);
                        BEGIN(INITIAL);
                      }

 /*------------nesting comments------------------*/

"/+"                         { yylloc->step();
                               commentBegin = yylloc->begin;
                               yylloc->columns(yyleng);
                               ++nestingCommentDepth;
                               BEGIN(nestingcomment);
                             }

<nestingcomment><<EOF>>      { if (yyextra->messages)
                               	yyextra->messages->add("unterminated-comment", CodeLocation(commentBegin, yylloc->end));
                               return token::END;
                             }

<nestingcomment>"/+"         { yylloc->step();
                               yylloc->columns(yyleng);
                               ++nestingCommentDepth;
                             }

<nestingcomment>[^+/\n]*     { yylloc->step();
                               yylloc->columns(yyleng);
                             }

<nestingcomment>"/"+[^+/\n]* { yylloc->step();
                               yylloc->columns(yyleng);
                             }

<nestingcomment>"+"+[^+/\n]* { yylloc->step();
                               yylloc->columns(yyleng);
                             }

<nestingcomment>[\n]+        { yylloc->step();
	                           yylloc->lines(yyleng);
	                         }

<nestingcomment>"+/"         { yylloc->step();
                               yylloc->columns(yyleng);
                               --nestingCommentDepth;
	                           if (nestingCommentDepth == 0) BEGIN(INITIAL);
	                         }

 /*------------line comments---------------------*/

"//"                { yylloc->step();
                      yylloc->columns(yyleng);
                      BEGIN(linecomment);
                    }

<linecomment>[^\n]* { yylloc->step();
                      yylloc->columns(yyleng);
                    }

<linecomment>[\n]   { yylloc->step();
	                  yylloc->lines(yyleng);
	                  BEGIN(INITIAL);
	                }

 /*------------end of file-----------------------*/

<<EOF>> return token::END;

 /*------------catch-all-------------------------*/

. { yylloc->step();
    yylloc->columns(yyleng);
    if (yyextra->messages)
    	yyextra->messages->add("unexpected-character", *yylloc) % yytext;
  }
